"""
Base model classes for specification fixing.

Provides common functionality and interfaces for all data models.
"""

from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, Any, Optional, List
import json

from ..utils.exceptions import ValidationError
from ..utils.logger import get_logger

logger = get_logger(__name__)


class BaseModel(ABC):
    """Base class for all data models."""

    def __init__(self, **kwargs):
        """Initialize model with keyword arguments."""
        self._data = {}
        self._load_data(kwargs)

    def _load_data(self, data: Dict[str, Any]) -> None:
        """Load data into model."""
        for key, value in data.items():
            setattr(self, key, value)

    @abstractmethod
    def validate(self) -> None:
        """Validate model data. Must be implemented by subclasses."""
        pass

    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary."""
        result = {}
        for key, value in self.__dict__.items():
            if not key.startswith('_'):
                if isinstance(value, BaseModel):
                    result[key] = value.to_dict()
                elif isinstance(value, list) and all(isinstance(x, BaseModel) for x in value):
                    result[key] = [x.to_dict() for x in value]
                elif isinstance(value, datetime):
                    result[key] = value.isoformat()
                else:
                    result[key] = value
        return result

    def to_json(self) -> str:
        """Convert model to JSON string."""
        return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]):
        """Create model instance from dictionary."""
        return cls(**data)

    @classmethod
    def from_json(cls, json_str: str):
        """Create model instance from JSON string."""
        data = json.loads(json_str)
        return cls.from_dict(data)

    def __repr__(self) -> str:
        """String representation of model."""
        class_name = self.__class__.__name__
        attrs = []
        for key, value in self.__dict__.items():
            if not key.startswith('_'):
                attrs.append(f"{key}={value!r}")
        return f"{class_name}({', '.join(attrs)})"


class TimestampedModel(BaseModel):
    """Base model with timestamp fields."""

    def __init__(self, **kwargs):
        """Initialize with timestamps."""
        now = datetime.now()
        kwargs.setdefault('created_at', now)
        kwargs.setdefault('updated_at', now)
        super().__init__(**kwargs)

    @property
    def created_at(self) -> datetime:
        """Get creation timestamp."""
        return self._data.get('created_at')

    @created_at.setter
    def created_at(self, value: datetime) -> None:
        """Set creation timestamp."""
        if isinstance(value, str):
            value = datetime.fromisoformat(value)
        self._data['created_at'] = value

    @property
    def updated_at(self) -> datetime:
        """Get update timestamp."""
        return self._data.get('updated_at')

    @updated_at.setter
    def updated_at(self, value: datetime) -> None:
        """Set update timestamp."""
        if isinstance(value, str):
            value = datetime.fromisoformat(value)
        self._data['updated_at'] = value

    def touch(self) -> None:
        """Update the updated_at timestamp."""
        self.updated_at = datetime.now()


class ValidatedModel(BaseModel):
    """Base model with validation capabilities."""

    def __init__(self, **kwargs):
        """Initialize and validate."""
        super().__init__(**kwargs)
        self.validate()

    @abstractmethod
    def get_validation_rules(self) -> Dict[str, List[str]]:
        """Get validation rules for this model."""
        return {}

    def validate(self) -> None:
        """Validate model against rules."""
        errors = []
        rules = self.get_validation_rules()

        for field, field_rules in rules.items():
            value = getattr(self, field, None)
            for rule in field_rules:
                error = self._validate_field(field, value, rule)
                if error:
                    errors.append(error)

        if errors:
            error_msg = f"Validation failed: {'; '.join(errors)}"
            logger.error(error_msg)
            raise ValidationError(error_msg)

    def _validate_field(self, field: str, value: Any, rule: str) -> Optional[str]:
        """Validate a single field against a rule."""
        if rule == 'required' and (value is None or value == ''):
            return f"{field} is required"

        if rule.startswith('min_length:') and value is not None:
            min_len = int(rule.split(':')[1])
            if len(value) < min_len:
                return f"{field} must be at least {min_len} characters"

        if rule.startswith('max_length:') and value is not None:
            max_len = int(rule.split(':')[1])
            if len(value) > max_len:
                return f"{field} must be at most {max_len} characters"

        if rule.startswith('min:') and value is not None:
            min_val = float(rule.split(':')[1])
            if float(value) < min_val:
                return f"{field} must be at least {min_val}"

        if rule.startswith('max:') and value is not None:
            max_val = float(rule.split(':')[1])
            if float(value) > max_val:
                return f"{field} must be at most {max_val}"

        if rule == 'email' and value is not None:
            import re
            email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
            if not re.match(email_pattern, value):
                return f"{field} must be a valid email address"

        if rule == 'url' and value is not None:
            import re
            url_pattern = r'^https?://[^\s/$.?#].[^\s]*$'
            if not re.match(url_pattern, value):
                return f"{field} must be a valid URL"

        return None


class CollectionModel(BaseModel):
    """Base model for collections of items."""

    def __init__(self, items: Optional[List[Any]] = None, **kwargs):
        """Initialize collection."""
        self.items = items or []
        super().__init__(**kwargs)

    def add(self, item: Any) -> None:
        """Add item to collection."""
        self.items.append(item)

    def remove(self, item: Any) -> bool:
        """Remove item from collection."""
        try:
            self.items.remove(item)
            return True
        except ValueError:
            return False

    def find(self, predicate) -> Optional[Any]:
        """Find item matching predicate."""
        for item in self.items:
            if predicate(item):
                return item
        return None

    def filter(self, predicate) -> List[Any]:
        """Filter items matching predicate."""
        return [item for item in self.items if predicate(item)]

    def count(self) -> int:
        """Get item count."""
        return len(self.items)

    def is_empty(self) -> bool:
        """Check if collection is empty."""
        return len(self.items) == 0

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        result = super().to_dict()
        result['items'] = [
            item.to_dict() if isinstance(item, BaseModel) else item
            for item in self.items
        ]
        return result